﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Model;

namespace ReportBrowser.ViewModel
{
    internal class NamespaceViewModel : LanguageElementViewModelBase
    {
        #region Fields

        private static Dictionary<Namespace, NamespaceViewModel> sCallerInstances =
            new Dictionary<Namespace, NamespaceViewModel>();
        private static Dictionary<Namespace, NamespaceViewModel> sCalleeInstances =
            new Dictionary<Namespace, NamespaceViewModel>();

        #endregion Fields

        #region Lifecycle

        static NamespaceViewModel()
        {
            MessageViewModel.InstanceSelected += new System.EventHandler(MessageViewModel_InstanceSelected);
        }

        public NamespaceViewModel(Namespace model, bool isTruncatedAsCaller = false, bool isTruncatedAsCallee = false)
        {
            this.Model = model;
            this.Callees = new ObservableCollection<NamespaceViewModel>();
            this.Callers = new ObservableCollection<NamespaceViewModel>();
        }

        public static NamespaceViewModel GetInstance(Namespace model, bool isCaller)
        {
            if (isCaller == true)
            {
                if (sCallerInstances.ContainsKey(model) == false)
                    sCallerInstances.Add(model, new NamespaceViewModel(model));
                return sCallerInstances[model];
            }
            else if (isCaller == false)
            {
                if (sCalleeInstances.ContainsKey(model) == false)
                    sCalleeInstances.Add(model, new NamespaceViewModel(model));
                return sCalleeInstances[model];
            }
            else
                throw new System.ArgumentException();
        }

        public static bool IsInstantiated(Namespace model, bool isCaller)
        {
            if (isCaller == true)
                return sCallerInstances.ContainsKey(model);
            else if (isCaller == false)
                return sCalleeInstances.ContainsKey(model);
            else
                throw new System.ArgumentException();
        }

        private static void ClearInstances()
        {
            sCallerInstances = new Dictionary<Namespace, NamespaceViewModel>();
            sCalleeInstances = new Dictionary<Namespace, NamespaceViewModel>();
        }

        #endregion Lifecycle

        #region Properties

        public Namespace Model { get; private set; }

        private bool mIsCallersInitialized = false;
        private ObservableCollection<NamespaceViewModel> pCallers;
        public ObservableCollection<NamespaceViewModel> Callers
        {
            get
            {
                if (mIsCallersInitialized == false)
                {
                    foreach (var item in this.Model.CallGraphEdge_NamespaceDependency_Callee)
                    {

                        if (item.CallerNamespace != null)
                            pCallers.Add(GetInstance(item.CallerNamespace, isCaller: true));
                        else
                            Debug.WriteLine("(item in CallGraphEdge_NamespaceDependency_Callee).CallerNamespace == null");
                    }
                    mIsCallersInitialized = true;
                }
                return pCallers;
            }
            private set
            {
                if (value != pCallers)
                {
                    pCallers = value;
                    OnPropertyChanged("Callers");
                }
            }
        }

        private bool mIsCalleesInitialized = false;
        private ObservableCollection<NamespaceViewModel> pCallees;
        public ObservableCollection<NamespaceViewModel> Callees
        {
            get
            {
                if (mIsCalleesInitialized == false)
                {
                    foreach (var item in this.Model.CallGraphEdge_NamespaceDependency_Caller)
                    {

                        if (item.CalleeNamespace != null)
                            pCallees.Add(GetInstance(item.CalleeNamespace, isCaller: false));
                        else
                            Debug.WriteLine("(item in CallGraphEdge_NamespaceDependency_Caller).CalleeNamespace == null");
                    }
                    mIsCalleesInitialized = true;
                }
                return pCallees;
            }
            private set
            {
                if (value != pCallees)
                {
                    pCallees = value;
                    OnPropertyChanged("Callees");
                }
            }
        }

        #endregion Properties

        #region Methods

        /*
        private bool IsOnCycle(Namespace ns, bool asCaller)
        {
            HashSet<Namespace> gray = new HashSet<Namespace>();
            HashSet<Namespace> black = new HashSet<Namespace>();
            Debug.WriteLine("IsOnCycle: namespace=" + ns.Name + " asCaller=" + asCaller.ToString());
            List<Namespace> path = DFS(ns, asCaller, ref gray, ref black);
            if (path != null)
                Debug.WriteLine("path==<" + SetToString(path) + ">");
            return path != null;
        }

        private List<Namespace> DFS(Namespace ns, bool asCaller,
            ref HashSet<Namespace> gray, ref HashSet<Namespace> black)
        {
            gray.Add(ns);
            EntityCollection<CallGraphEdge_NamespaceDependency> neighbours;
            if (asCaller == true)
                neighbours = ns.CallGraphEdge_NamespaceDependency_Caller;
            else
                neighbours = ns.CallGraphEdge_NamespaceDependency_Callee;
            foreach (var item in neighbours)
            {
                Namespace neighbour;
                if (asCaller == true)
                    neighbour = item.CalleeNamespace;
                else
                    neighbour = item.CallerNamespace;
                if ((asCaller == true
                    && (NamespaceViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: false, isTruncatedAsCallee: false) == true
                            || NamespaceViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: true, isTruncatedAsCallee: false) == true))
                    || (asCaller == false
                        && (NamespaceViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: false, isTruncatedAsCallee: false) == true
                            || NamespaceViewModel.IsInstantiated(neighbour, isCaller: !asCaller,
                                isTruncatedAsCaller: false, isTruncatedAsCallee: true) == true)))
                {
                    if (gray.Contains(neighbour) == false && black.Contains(neighbour) == false)//white
                    {
                        List<Namespace> path = DFS(neighbour, asCaller, ref gray, ref black);
                        if (path != null)
                        {
                            path.Add(neighbour);
                            return path;
                        }
                    }
                    else if (gray.Contains(neighbour) == true && black.Contains(neighbour) == false)//gray
                    {
                        return new List<Namespace>() { neighbour };
                    }
                }
            }
            gray.Remove(ns);
            black.Add(ns);
            return null;
        }

        private string SetToString(IEnumerable<Namespace> set)
        {
            string s = "";
            foreach (var item in set)
            {
                s += item.Name + ", ";
            }
            return s.TrimEnd(new char[] { ' ', ',' });
        }
         */

        #endregion Methods

        #region EventHandlers

        static void MessageViewModel_InstanceSelected(object sender, System.EventArgs e)
        {
            ClearInstances();
        }

        #endregion EventHandlers
    }
}
